//author Sylvain Bertrand <digital.ragnarok@gmail.com>
//Protected by GNU Affero GPL v3 with some exceptions.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <error.h>
#include <sys/epoll.h>
#include <errno.h>

#include <linux/types.h>

#include <alga/pixel_fmts.h>
#include <alga/amd/dce6/dce6.h>
#include <alga/amd/si/ioctl.h>

#define e(m,...) error(0,0,m,##__VA_ARGS__)
#define o(m,...) printf(m "\n",##__VA_ARGS__)
#define unsignedl unsigned long

#define EVTS_MAX 10

int main(int argc, char *argv[])
{
  errno=0;
  int d_fd=open("/dev/si0", O_RDWR);
  if(d_fd==-1){
    e("open failed:%s\n",strerror(errno));
    return EXIT_FAILURE;
  }

  errno=0;
  int efd=epoll_create1(0);
  if(efd==-1){
    e("epoll_create:%s",strerror(errno));
    return EXIT_FAILURE;
  }

  struct epoll_event evt;
  evt.events=EPOLLIN;
  evt.data.fd=d_fd;
  errno=0;
  int r=epoll_ctl(efd,EPOLL_CTL_ADD,d_fd,&evt);
  if(r==-1){
    e("epoll_ctl:add driver file descriptor:%s",strerror(errno));
    return EXIT_FAILURE;
  }

  while (1) {
    uint8_t idx=strtoul(argv[1],NULL,10);
    unsignedl req=_IOW('d',SI_DCE_PF,idx);
    errno=0;
    r=ioctl(d_fd,req,&idx);
    if(r==-1){
      e("pf failed:%s",strerror(errno));
      return EXIT_FAILURE;
    }

    struct epoll_event evts[EVTS_MAX];
    memset(&evts,0,sizeof(evts));
    errno=0;
    int fds_n=epoll_wait(efd,&evts[0],EVTS_MAX,-1);
    if(fds_n==-1){
      e("epoll_wait:%s",strerror(errno));
      return EXIT_FAILURE;
    }

    o("fds_n=%u",fds_n);

    int fd=0;
    while(1){
      if(fd==fds_n) break;
      if(evts[fd].data.fd==d_fd){
        struct si_evt si_evt;

        o("found driver fd");

        //XXX:the driver file has no end and read will block if there is no
        //event available.
        errno=0;
        ssize_t rd=read(d_fd,&si_evt,sizeof(si_evt));
        o("read one event of %ld bytes",(long)rd);
        if(rd==-1){
          e("read:%s",strerror(errno));
          return EXIT_FAILURE;
        }
        struct si_evt_pf *si_evt_pf=&si_evt.params.pf;
        struct timespec *tp=&si_evt.params.pf.monotonic_raw_tp;
        o("pf event on dp%u vblanks_n=0x%08x sec=%lu nsec=%lu",si_evt_pf->idx,
                                     si_evt_pf->vblanks_n,tp->tv_sec,tp->tv_nsec);
      }
      ++fd;
    }
  }
  return EXIT_SUCCESS;
}
